using System;
using FPLibrary;
using UnityEngine;
using PathX;

namespace PathX.Unity
{
    public class PathGridComponent : MonoBehaviour
    {
        private IGrid grid;
        
        [SerializeField]
        private CellMatrixData matrixData;
        
        //size along the x axis
        public int sizeX = 10;

        //size along the z axis
        public int sizeZ = 10;

        /// <summary>
        /// The cell size
        /// </summary>
        // [MinCheck(0.1f, tooltip = "The size of each grid cell, expressed as the length of one side.")]
        public float cellSize = 2f;
        
        /// <summary>
        /// The obstacle sensitivity range, meaning any obstacle within this range of the cell center will cause the cell to be blocked.
        /// </summary>
        // [MinCheck(0f, tooltip = "How close to the center of a cell must an obstacle be to block the cell.")]
        public float obstacleSensitivityRange = 0.5f;

        /// <summary>
        /// The baked grid data
        /// </summary>
        [SerializeField, HideInInspector] public CellMatrixData bakedData;

        //In order to prevent causing a breaking change with the introduction of the new editor,
        //the properties that has always been exposed as properties remain that way
        [SerializeField, Tooltip("The center of the grid.")]
        private Vector3 _origin;

        [SerializeField] // Label("Offset", "The offset in relation to the linked transform.")]
        private Vector3 _originOffset;

        [SerializeField, Tooltip("An optional friendly name for the grid that will be used in messages and such.")]
        private string _friendlyName;

        [SerializeField, Tooltip("Link the center of the grid to the position of the game object.")]
        private bool _linkOriginToTransform = true;

        private Bounds _bounds;

        /// <summary>
        /// Gets or sets the friendly name of the grid. Used in messages and such.
        /// </summary>
        public string friendlyName
        {
            get { return _friendlyName; }
            set { _friendlyName = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to link origin to the transform.
        /// </summary>
        public bool linkOriginToTransform
        {
            get { return _linkOriginToTransform; }
            set { _linkOriginToTransform = value; }
        }

        /// <summary>
        /// The origin, i.e. center, of the grid
        /// </summary>
        public Vector3 origin
        {
            get
            {
                if (_linkOriginToTransform)
                {
                    return this.transform.position + _originOffset;
                }

                return _origin;
            }

            set
            {
                if (!_linkOriginToTransform)
                {
                    _origin = value;
                }
            }
        }

        /// <summary>
        /// Gets or sets the origin offset. This will offset the origin if <see cref="linkOriginToTransform"/> is true.
        /// </summary>
        /// <value>
        /// The origin offset.
        /// </value>
        public Vector3 originOffset
        {
            get { return _originOffset; }
            set { _originOffset = value; }
        }

        /// <summary>
        /// Gets the bounds of the grid.
        /// </summary>
        public Bounds bounds
        {
            get
            {
                // //While the grid has yet to be initialized we have to recalculate the bounds since the grid component may be moved around before being initialized
                if (this.grid == null)
                {
                    CalculateBounds();
                }

                return _bounds;
            }
        }

        public IGrid editorGrid
        {
            get
            {
                return grid;
            }
        }

        public CellMatrixConfiguration GetCellMatrixConfiguration()
        {
            return new CellMatrixConfiguration()
            {
                origin = FPVector.ToFPVector(this._origin),
                sizeX = this.sizeX,
                sizeZ = this.sizeZ,
                cellSize = this.cellSize,
                bounds = FPBB.ToFPBB(this.bounds)
            };
        }
        
        //用于editor模式下填充数据
        internal Bounds EnsureForEditor(bool refresh, Bounds area)
        {
            if (!Application.isPlaying)
            {
                //We do it this way rather than incorporate it below, since recalc of bounds requires grid to be null
                if (refresh && this.transform.hasChanged)
                {
                    this.transform.hasChanged = false;
                    this.grid = null;
                }

                if (this.grid == null)
                {
                    var builder = GetCellMatrixConfiguration();
                    this.grid = builder.Create();
                }
            }

            var gb = this.bounds;
            gb.Expand(-this.cellSize);

            var bl = new Vector3(Mathf.Max(area.min.x, gb.min.x), 0f, Mathf.Max(area.min.z, gb.min.z));
            var tr = new Vector3(Mathf.Min(area.max.x, gb.max.x), 0f, Mathf.Min(area.max.z, gb.max.z));
            area.SetMinMax(bl, tr);

            if (refresh && !Application.isPlaying)
            {
                this.grid.cellMatrix.UpdateForEditor(area, this.bakedData);
            }

            return area;
        }
        
        internal void EnsureGrid()
        {
            if (this.grid == null)
            {
                var builder = GetCellMatrixConfiguration();

                if (this.bakedData != null)
                {
                    this.grid = builder.Create(this.bakedData);
                }
                else
                {
                    this.grid = builder.Create();
                }
            }
        }

        private void CalculateBounds()
        {
            var boundsCenter = this.origin;

            _bounds = new Bounds(boundsCenter, new Vector3(this.sizeX * this.cellSize, 0.01f, this.sizeZ * this.cellSize));
        }
    }
}